package com.zbs.framework.pay.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.alipay.easysdk.factory.Factory;
import com.alipay.easysdk.kernel.Config;
import com.alipay.easysdk.payment.app.models.AlipayTradeAppPayResponse;
import com.alipay.easysdk.payment.common.models.*;
import com.alipay.easysdk.payment.facetoface.models.AlipayTradePayResponse;
import com.alipay.easysdk.payment.facetoface.models.AlipayTradePrecreateResponse;
import com.alipay.easysdk.payment.page.models.AlipayTradePagePayResponse;
import com.alipay.easysdk.payment.wap.models.AlipayTradeWapPayResponse;
import com.zbs.framework.common.utils.ErrorAssert;
import com.zbs.framework.common.utils.ReadTxtUtil;
import com.zbs.framework.pay.config.AliPayConfig;
import com.zbs.framework.pay.event.AlipayCallbackModel;
import com.zbs.framework.pay.qrcode.QrCodeResponse;
import com.zbs.framework.pay.service.AliPayNewService;
import com.zbs.framework.pay.util.QrUtils;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Map;
import java.util.Objects;

@Service
@Transactional
@DependsOn("aliPayConfig")
public class AliPayNewServiceImpl implements AliPayNewService {

    public AliPayNewServiceImpl() {
        //设置Factory 全局参数
        Config config = new Config();
        config.appId = AliPayConfig.appId;
        if (StringUtils.hasLength(AliPayConfig.alipayPublicKey) && StringUtils.hasLength(AliPayConfig.merchantPrivateKey)) {
            config.merchantPrivateKey = AliPayConfig.merchantPrivateKey;
            config.alipayPublicKey = AliPayConfig.alipayPublicKey;
        } else {
            File merchantPrivateKeyFile = new File(AliPayConfig.merchantPrivateKeyPath);
            File alipayPublicKeyFile = new File(AliPayConfig.alipayPublicKeyPath);
            ErrorAssert.isTrue(merchantPrivateKeyFile.exists(),"支付宝私钥txt文件路径不合法");
            ErrorAssert.isTrue(alipayPublicKeyFile.exists(),"支付宝公钥txt文件路径不合法");
            try {
                config.merchantPrivateKey = Objects.requireNonNull(ReadTxtUtil.readTxtFile(AliPayConfig.merchantPrivateKeyPath)).toString();
                config.alipayPublicKey = Objects.requireNonNull(ReadTxtUtil.readTxtFile(AliPayConfig.alipayPublicKeyPath)).toString();
            } catch (IOException e) {
                e.printStackTrace();
                ErrorAssert.error("读取支付宝秘钥异常，请检查是否是txt文件");
            }
        }
        config.gatewayHost = AliPayConfig.gatewayUrl;
        config.protocol = "https";
        config.signType = AliPayConfig.signType;
        config.notifyUrl = AliPayConfig.notifyUrl;
        if (StringUtils.hasLength(AliPayConfig.encryptKey)) {
            config.encryptKey = AliPayConfig.encryptKey;
        }
        //可设置AES密钥，调用AES加解密相关接口时需要（可选）
        Factory.setOptions(config);

    }

    /**
     * 对应 alipay.trade.app.pay 接口
     * 构造交易数据供商户app到支付宝下单
     */
    @Override
    public QrCodeResponse createAppTradeForm(String subject, String tradeNo, BigDecimal totalAmount, String express) throws Exception {
        //参数校验
        this.checkPay(subject,tradeNo,totalAmount,express);
        AlipayTradeAppPayResponse response = Factory.Payment.App()
                //单独设置超时时间 默认15分钟
                .optional("timeout_express", express)
                .pay(subject, tradeNo, totalAmount.toPlainString());
        return QrUtils.qrcodePay(response);
    }

    /**
     * 对应alipay.trade.page.pay 接口
     * 当面付 扫码
     */
    @Override
    public QrCodeResponse createFaceToFace(String subject , String tradeNo, BigDecimal totalAmount, String express, String authCode) throws Exception{
        //参数校验
        this.checkPay(subject,tradeNo,totalAmount,express);

        AlipayTradePayResponse response = Factory.Payment.FaceToFace()
                .optional("timeout_express", express)
                .pay(subject, tradeNo, totalAmount.toPlainString(), authCode);
        return QrUtils.qrcodePay(response);
    }

    @Override
    public QrCodeResponse createFaceToFace(String subject, String tradeNo, BigDecimal totalAmount, String express) throws Exception {
        //参数校验
        this.checkPay(subject,tradeNo,totalAmount,express);

        AlipayTradePrecreateResponse response = Factory.Payment.FaceToFace()
                .optional("timeout_express", express)
                .preCreate(subject, tradeNo, totalAmount.toPlainString());
        return QrUtils.qrcodePay(response);
    }

    /**
     * 对应alipay.trade.page.pay 接口
     * 构造交易数据供pc端到支付宝下单
     */
    @Override
    public QrCodeResponse createWebTradeForm(String subject , String tradeNo, BigDecimal totalAmount,String express, String returnUrl) throws Exception{
        //参数校验
        this.checkPay(subject,tradeNo,totalAmount,express);

        AlipayTradePagePayResponse response = Factory.Payment.Page()
                //单独设置超时时间 默认15分钟
                .optional("timeout_express", express)
                .pay(subject, tradeNo, totalAmount.toPlainString(), returnUrl);
        return QrUtils.qrcodePay(response);
    }

    /**
     *  alipay.trade.wap.pay
     *  构造交易数据供wap端到支付宝下单
     */
    @Override
    public QrCodeResponse createWapTradeForm(String subject , String tradeNo, BigDecimal totalAmount, String express, String quitUrl, String returnUrl) throws Exception{
        //参数校验
        this.checkPay(subject,tradeNo,totalAmount,express);

        AlipayTradeWapPayResponse response = Factory.Payment.Wap()
                //单独设置超时时间 默认15分钟
                .optional("timeout_express", express)
                .pay(subject, tradeNo, totalAmount.toPlainString(), quitUrl, returnUrl);
        return QrUtils.qrcodePay(response);
    }

    /**
     * 对应alipay.trade.query(统一收单线下交易查询)
     */
    @Override
    public AlipayCallbackModel queryTrade(String tradeNo,String outTradeNo) throws Exception {
        if (!StringUtils.hasLength(tradeNo) && !StringUtils.hasLength(outTradeNo)){
            ErrorAssert.error("订单号和回执单号不可同时为空");
        }
        AlipayTradeQueryResponse response = Factory.Payment.Common().optional("trade_no", tradeNo).query(outTradeNo);
        JSONObject jsonObject = JSONObject.parseObject(response.getHttpBody());
        return JSONObject.parseObject(jsonObject.getString("alipay_trade_query_response"), AlipayCallbackModel.class);
    }

    /**
     * 交易撤销
     * alipay.trade.cancel
     */
    @Override
    public AlipayTradeCancelResponse cancelTrade(String tradeNo) throws Exception{
        return Factory.Payment.Common().cancel(tradeNo);
    }

    /**
     * alipay.trade.close(统一收单交易关闭接口)
     */
    @Override
    public AlipayTradeCloseResponse closeTrade(String tradeNo) throws Exception{
        return Factory.Payment.Common().close(tradeNo);
    }

    /**
     * alipay.trade.refund(统一收单交易退款接口)
     */
    @Override
    public String refundTrade(String tradeNo, String outTradeNo, BigDecimal refundAmount) throws Exception{
        if (!StringUtils.hasLength(tradeNo) && !StringUtils.hasLength(outTradeNo)){
            ErrorAssert.error("订单号和回执单号不可同时为空");
        }
        AlipayTradeRefundResponse response = Factory.Payment.Common().optional("trade_no", tradeNo).refund(outTradeNo, refundAmount.toPlainString());
        if (!response.code.equals("10000")){
            ErrorAssert.error(response.getSubMsg());
        }
        return response.tradeNo;
    }


    @Override
    public boolean verifyNotify(Map<String, String> parameters) throws Exception {
        return  Factory.Payment.Common().verifyNotify(parameters);
    }

    /**
     * alipay.trade.fastpay.refund.query(统一收单交易退款查询)
     */
    @Override
    public AlipayTradeFastpayRefundQueryResponse refundQuery(String tradeNo, String outRequestNo) throws Exception{
        return Factory.Payment.Common().queryRefund(tradeNo,outRequestNo);
    }

    private void checkPay(String subject , String tradeNo, BigDecimal totalAmount,String express){
        ErrorAssert.trimHasLength(subject,"产品标题不能为空");
        ErrorAssert.notNull(totalAmount,"支付金额不能为空");
        ErrorAssert.trimHasLength(express,"支付超时时间不能为空");
        ErrorAssert.trimHasLength(tradeNo,"订单号不能为空");
    }
}
